package cluster

import (
	"io/ioutil"
	"stathat.com/c/consistent" // 散列表
	"github.com/hashicorp/memberlist" // gossip协议
	"time"
)

type Node interface {
	// ShouldProcess接收一个 string类型的参数 key，用来 告诉节点该 key 是否应该由自己处理
	ShouldProcess(key string) (string,bool)
	// Members 方法用来提供整个集群的节点列表
	Members() []string
	// Addr 方法用来获取本节点地址
	Addr() string
}

type node struct {
	*consistent.Consistent
	addr string	// 记录本本节点地址
}

func (n *node) Addr() string  {
	return n.addr
}

func (n *node) ShouldProcess(key string) (string,bool)  {
	addr,_ := n.Get(key)
	return addr,addr == n.addr
}

func New(addr,cluster string) (Node,error)  {
	// 建立在局域网上的缓存服务
	conf := memberlist.DefaultLANConfig()
	conf.Name = addr	// 节点名字
	conf.BindAddr = addr	// gossip 监听地址，命令行参数中的本地节点地址
	conf.LogOutput = ioutil.Discard	// 日志输出器，实现write方法，写入的内容将被丢弃
	l,e := memberlist.Create(conf)
	if e != nil {
		return nil,e
	}
	if cluster == "" {
		cluster = addr
	}
	clu := []string{cluster}
	_,e = l.Join(clu)
	if e != nil {
		return nil,e
	}
	circle := consistent.New()
	// 虚拟节点设置为 256
	circle.NumberOfReplicas = 256
	go func() {
		for {
			m := l.Members()
			nodes := make([]string,len(m))
			for i,n := range m {
				nodes[i] = n.Name
			}
			circle.Set(nodes)
			time.Sleep(time.Second)
		}
	}()
	return &node{circle,addr},nil
}






















